# coding: utf-8

# -------------------------------------------------------------------------------
# Name:         ext_method_runner.py
# Description:  扩展方法执行器
# Author:       XiangjunZhao
# EMAIL:        2419352654@qq.com
# Date:         2020/12/10 15:38
# -------------------------------------------------------------------------------
import builtins
import json
import logging
import types

from django.utils import timezone

from apps.HttpAutoTestService.core import ext_methods
from apps.HttpAutoTestService.core.ext_methods import *
from apps.HttpAutoTestService.core.extractor import Extractor
from apps.HttpAutoTestService.core.parser import regex_replace_variables, regex_replace_functions
from apps.HttpAutoTestService.core.validator import Validator
from apps.HttpAutoTestService.models import ExtMethodResult

logger = logging.getLogger(__name__)


class ExtMethodRunner(object):

    def __init__(self):
        # 扩展函数
        self.ext_methods = {}
        # 系统内置函数
        self.builtins = {}
        for name, item in vars(ext_methods).items():
            if isinstance(item, types.FunctionType):
                self.ext_methods[name] = item

        for name, item in vars(builtins).items():
            if isinstance(item, types.BuiltinFunctionType):
                self.builtins[name] = item

    def parse_ext_method_args(self, content, variables_mapping):
        '''
        扩展方法参数解析
        :param content:
        :param variables_mapping:
        :return:
        '''
        content = regex_replace_variables(content=content, variables_mapping=variables_mapping)
        content = regex_replace_functions(content=content)
        return content

    def run(self, http_session_context=None, ext_method_name=None, ext_method=None, expect_result=None, executor=None,
            is_periodictask=True, loop_count=1):
        '''
        执行扩展方法
        :param http_session_context:
        :param ext_method:需要执行的扩展方法名称
        :param ext_method:需要执行的扩展方法
        :param expect_result:期望结果
        :param executor:执行者
        :param is_periodictask:是否是定时任务
        :param loop_count:循环次数
        :return:
        '''
        # 执行时间
        variables_mapping = {**http_session_context.session_variables_mapping,
                             **http_session_context.output_variables_mapping}
        ext_method = self.parse_ext_method_args(content=ext_method, variables_mapping=variables_mapping)
        logger.info('执行{}扩展方法'.format(ext_method))

        ext_method_result_list = []

        method_name = ext_method.split('(')[0]
        if method_name in self.ext_methods or method_name in self.builtins:
            # 期望结果
            expect_result = self.parse_ext_method_args(content=expect_result, variables_mapping=variables_mapping)
            expect_result = json.loads(expect_result) if expect_result else {}
            logger.info('期望结果：{expect_result}'.format(expect_result=expect_result))
            # 循环执行设定次数
            for i in range(loop_count):
                execute_time = timezone.now()
                result = eval(ext_method)

                if result and isinstance(expect_result, dict) and expect_result.get('output'):
                    # 创建提取器
                    extractor = Extractor()
                    # 统一格式化扩展方法的输出变量output
                    extractor.uniform_output(expect_result.get("output"))
                    # 提取输出变量
                    output_mapping = extractor.extract_output(resp_obj=result)
                    # 将提取的输出变量值更新到http_session_context实例的update_output_variables变量中
                    http_session_context.update_output_variables(variables_mapping=output_mapping)

                validate_pass = failure_reason = None
                if isinstance(expect_result, dict) and expect_result.get('validate', None):
                    # 创建校验器
                    validator = Validator()
                    # 统一格式化扩展方法的校验变量validate
                    validator.uniform_validate(expect_result.get("validate"))
                    variables_mapping = {**http_session_context.session_variables_mapping,
                                         **http_session_context.output_variables_mapping}
                    validate_variables_list = self.parse_ext_method_args(content=validator.validate_variables_list,
                                                                         variables_mapping=variables_mapping)
                    validator.validate_variables_list = json.loads(
                        validate_variables_list) if validate_variables_list else []
                    validate_pass, failure_reason = validator.validate(resp_obj=result)

                ext_method_result_mapping = {
                    "ext_method_name": ext_method_name or method_name,
                    "ext_method": ext_method,
                    "ext_method_run_result": json.dumps(result, ensure_ascii=False),
                    "execute_time": execute_time,
                    "status": validate_pass,
                    "failure_reason": failure_reason
                }

                if not is_periodictask:
                    ext_method_result_mapping.update({
                        'executor_id': executor.id,
                        'executor_real_name': executor.real_name,
                        'is_periodictask': is_periodictask
                    })

                if validate_pass == 'FAIL':
                    logger.warning('扩展方法：{method_name}，测试未通过；原因：{failure_reason}'.format(method_name=method_name,
                                                                                         failure_reason=failure_reason))
                    ext_method_result_mapping.update({
                        'failure_reason': json.dumps(failure_reason, ensure_ascii=False)
                    })
                ext_method_result = ExtMethodResult(**ext_method_result_mapping)
                ext_method_result_list.append(ext_method_result)
        return ext_method_result_list
